package com.ceair.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ceair.entity.SysRole;
import com.ceair.entity.SysRoleAuthority;
import com.ceair.entity.SysUserRole;
import com.ceair.entity.request.role.DeleteRoleReq;
import com.ceair.entity.request.role.QueryRoleReq;
import com.ceair.entity.request.role.RoleInOrUpReq;
import com.ceair.entity.request.role.UpdateRoleAuthorityReq;
import com.ceair.entity.vo.role.RoleAuthorityVO;
import com.ceair.entity.vo.role.SysRoleVO;
import com.ceair.enums.DeleteStatus;
import com.ceair.enums.ResultCode;
import com.ceair.exception.BusinessException;
import com.ceair.mapper.SysRoleMapper;
import com.ceair.service.ISysAuthorityService;
import com.ceair.service.ISysRoleAuthorityService;
import com.ceair.service.ISysRoleService;
import com.ceair.service.ISysUserRoleService;
import com.ceair.utils.structMapper.SysRoleStructMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * <p>
 * 系统角色表 服务实现类
 * </p>
 *
 * @author wangbaohai
 * @since 2025-02-25
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {

    private final ISysAuthorityService sysAuthorityService;
    private final ISysRoleAuthorityService sysRoleAuthorityService;
    private final ISysUserRoleService sysUserRoleService;

    /**
     * 重写查询角色列表的方法，使用分页查询
     *
     * @param queryRoleReq 包含角色查询条件和分页信息的请求对象
     * @return 返回分页查询结果，包含角色信息
     */
    @Override
    public Page<SysRole> queryRoleWithPage(QueryRoleReq queryRoleReq) {
        // 初始化分页工具以及参数
        Page<SysRole> page = new Page<>(queryRoleReq.getPageReq().getCurrent(), queryRoleReq.getPageReq().getSize());

        // 调用mapper分页查询
        return this.baseMapper.queryRoleWithPage(page, queryRoleReq);
    }

    /**
     * 保存角色信息
     * <p>
     * 此方法用于处理角色的创建或更新请求它首先将请求中的数据转换到SysRole实体类中，
     * 然后根据角色ID判断是进行插入还是更新操作最后，将保存或更新后的角色信息转换为VO对象返回
     *
     * @param roleInOrUpReq 角色创建或更新请求对象，包含需要保存的角色信息
     * @return 保存或更新后的角色信息的视图对象
     */
    @Override
    public SysRoleVO save(RoleInOrUpReq roleInOrUpReq) {
        // 初始化SysRole对象，用于存储角色信息
        SysRole sysRole = new SysRole();

        // 将 roleInOrUpReq 中不是空的元素复制到 sysRole 中，避免空值覆盖已有数据
        BeanUtil.copyProperties(roleInOrUpReq, sysRole, CopyOptions.create().ignoreNullValue());

        // 直接使用 saveOrUpdate 方法，如果存在则更新，不存在则插入
        saveOrUpdate(sysRole);

        // 将保存后的用户对象转换为VO对象返回，便于前端展示或后续操作
        return SysRoleStructMapper.INSTANCE.toVO(sysRole);
    }

    /**
     * 根据角色ID查询角色权限信息
     * <p>
     * 此方法首先获取系统中的所有权限信息，然后构建权限树，并根据给定的角色ID查询该角色的权限
     * 最后，它将在权限树中标记该角色拥有的权限，以便在界面上进行展示
     *
     * @param roleId 角色ID，用于查询特定角色的权限信息
     * @return 返回一个包含角色权限信息的列表
     */
    @Override
    public List<RoleAuthorityVO> queryRoleAuthority(Long roleId) {
        // 获取所有菜单
        List<RoleAuthorityVO> roleAuthorityVOS = sysAuthorityService.queryAllAuthority();

        // 根据 roleId 查询角色权限
        List<Long> sysRoleAuthorities = sysRoleAuthorityService.lambdaQuery().eq(SysRoleAuthority::getRoleId, roleId)
                .list().stream().filter(Objects::nonNull)
                .map(SysRoleAuthority::getAuthorityId).toList();

        // 如果没有找到对应的角色权限，直接返回
        if (sysRoleAuthorities.isEmpty()) {
            return roleAuthorityVOS;
        }

        // roleAuthorityVOS的 id 在 sysRoleAuthorities里，则将 select 设置为 true，并递归子数据
        setSelect(roleAuthorityVOS, sysRoleAuthorities);

        return roleAuthorityVOS;
    }

    /**
     * 更新角色权限
     *
     * @param updateRoleAuthorityReq 更新角色权限的请求对象，包含角色ID和权限ID列表
     * @return 返回更新是否成功
     * <p>
     * 此方法使用事务管理，确保在更新过程中发生异常时进行回滚
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateRoleAuthority(UpdateRoleAuthorityReq updateRoleAuthorityReq) {
        try {
            // 删除该角色的权限
            sysRoleAuthorityService.lambdaUpdate()
                    .eq(SysRoleAuthority::getRoleId, updateRoleAuthorityReq.getId()).remove();

            // 如果传入权限数据是空的直接返回true
            if (updateRoleAuthorityReq.getAuthorityIds().isEmpty()) {
                return true;
            }

            // 构建批量插入的数据
            List<SysRoleAuthority> sysRoleAuthorities =
                    updateRoleAuthorityReq.getAuthorityIds().stream().map(authorityId -> {
                        SysRoleAuthority sysRoleAuthority = new SysRoleAuthority();
                        sysRoleAuthority.setRoleId(updateRoleAuthorityReq.getId());
                        sysRoleAuthority.setAuthorityId(authorityId);
                        return sysRoleAuthority;
                    }).toList();

            // 批量插入
            return sysRoleAuthorityService.saveBatch(sysRoleAuthorities);
        } catch (Exception e) {
            log.error("更新角色权限失败", e);
            throw new BusinessException(ResultCode.FAILED.getCode(), e.getMessage());
        }
    }

    /**
     * 删除角色
     * <p>
     * 此方法用于删除一个或多个角色，确保在删除角色之前，与之相关的所有用户角色绑定关系和角色权限绑定关系都已被清理
     * 如果传入的角色ID列表为空或null，或者存在未清理的绑定关系，则删除操作失败，并抛出异常
     *
     * @param deleteRoleReq 包含要删除的角色ID列表的请求对象
     * @return 返回删除操作是否成功
     * @throws BusinessException 如果删除条件不满足，则抛出业务异常
     */
    @Override
    @Transactional
    public Boolean delete(DeleteRoleReq deleteRoleReq) {
        // 明确要删除的角色id清单
        List<Long> ids = deleteRoleReq.getIds();
        if (ids == null || ids.isEmpty()) {
            log.warn("传入的角色ID列表为空或null，删除操作失败");
            throw new BusinessException(ResultCode.FAILED.getCode(), "传入的角色ID列表为空或null，删除操作失败");
        }

        // 根据 id 查询用户角色绑定关系 校验是否存在未清楚的绑定关系
        List<SysUserRole> sysUserRoles = sysUserRoleService.lambdaQuery()
                .in(SysUserRole::getRoleId, ids)
                .list();

        // 检查是否有用户角色绑定关系存在，如果有，则删除操作失败
        if (!sysUserRoles.isEmpty()) {
            log.info("有用户-角色绑定关系未清理，删除角色失败");
            throw new BusinessException(ResultCode.FAILED.getCode(), "有用户-角色绑定关系未清理，删除角色失败");
        }

        // 根据 id 查询角色权限绑定关系 校验是否存在未清楚的绑定关系
        List<SysRoleAuthority> sysRoleAuthorities = sysRoleAuthorityService.lambdaQuery()
                .in(SysRoleAuthority::getRoleId, ids)
                .list();

        // 检查是否有角色权限绑定关系存在，如果有，则删除操作失败
        if (!sysRoleAuthorities.isEmpty()) {
            log.info("有角色-权限绑定关系未清理，删除角色失败");
            throw new BusinessException(ResultCode.FAILED.getCode(), "有角色-权限绑定关系未清理，删除角色失败");
        }

        // 构建批量逻辑删除角色对象
        List<SysRole> sysRoles = ids.stream().map(id -> {
            SysRole sysRole = new SysRole();
            sysRole.setId(id);
            sysRole.setDeleted(DeleteStatus.DELETED.getCode());
            return sysRole;
        }).toList();

        // 执行批量逻辑删除
        return updateBatchById(sysRoles);
    }

    /**
     * 根据用户ID查询角色ID清单
     *
     * @param userId 用户ID，用于查询角色ID
     * @return 用户的角色ID清单，如果用户没有角色或查询失败，则返回空列表
     */
    @Override
    public List<Long> queryRoleIdsByUserId(Long userId) {
        // 参数判空
        if (userId == null) {
            return List.of();
        }

        try {
            // 根据用户ID查询用户角色绑定关系，并只获取角色ID字段
            List<SysUserRole> sysUserRoles = sysUserRoleService.lambdaQuery()
                    .select(SysUserRole::getRoleId)  // 只查询需要的字段
                    .eq(SysUserRole::getUserId, userId)
                    .list();

            // 返回用户角色ID清单
            return sysUserRoles.stream().map(SysUserRole::getRoleId).toList();
        } catch (Exception e) {
            log.error("根据用户ID查询用户角色ID清单失败，失败原因：", e);
        }

        // 如果查询过程中出现异常，返回空列表
        return List.of();
    }

    /**
     * 递归设置角色权限信息中的选中状态
     * 此方法主要用于将角色权限数据中与系统角色权限匹配的项标记为选中状态
     *
     * @param roleAuthorityVOS   角色权限信息列表，用于展示层级结构的角色权限数据
     * @param sysRoleAuthorities 系统角色权限ID列表，表示当前角色拥有的权限
     */
    private void setSelect(List<RoleAuthorityVO> roleAuthorityVOS, List<Long> sysRoleAuthorities) {
        // 检查输入参数的有效性，如果任一参数为null，则直接返回，不进行处理
        if (roleAuthorityVOS == null || sysRoleAuthorities == null) {
            return;
        }

        // 将 sysRoleAuthorities 转换为 Set 以提高查找效率
        Set<Long> roleAuthoritySet = new HashSet<>(sysRoleAuthorities);

        // 使用迭代器遍历，避免空指针异常
        for (RoleAuthorityVO roleAuthorityVO : roleAuthorityVOS) {
            // 如果当前节点的ID在角色权限集合中，则将其标记为选中
            roleAuthorityVO.setSelect(roleAuthoritySet.contains(roleAuthorityVO.getId()));
            // 递归设置子节点，确保子节点不为空
            if (roleAuthorityVO.getChildren() != null) {
                setSelect(roleAuthorityVO.getChildren(), sysRoleAuthorities);
            }
        }
    }

}
